package io.kimmking.gateway.route;

import com.alibaba.cloud.nacos.parser.NacosDataYamlParser;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import io.kimmking.gateway.Event.RouteChangeEvent;
import io.kimmking.gateway.config.NacosConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

/**
 * 通过nacos下发动态路由配置,监听Nacos中gateway-route配置
 */
@Component
@Slf4j
@ConditionalOnClass({NacosConfig.class})
public class DynamicRouteListenerByNacos {

    @Autowired
    RouteChangeEvent routeChangeEvent;

    @Autowired
    ConfigService configService;

    @Autowired
    NacosConfig nacosConfig;

    @Autowired
    NacosDataYamlParser nacosDataYamlParser;

    @PostConstruct
    public void init() {
        log.info("gateway route init...");
        try{
            if(configService == null){
                log.warn("initConfigService fail");
                return;
            }
            if (!nacosConfig.NACOS_ROUTE_DATA_ID.endsWith("yml")
                    && !nacosConfig.NACOS_ROUTE_DATA_ID.endsWith("yaml")) {
                log.error("dataId must be yml format!");
                return;
            }
            String configInfo = configService.getConfig(nacosConfig.NACOS_ROUTE_DATA_ID,
                    nacosConfig.NACOS_ROUTE_GROUP, NacosConfig.DEFAULT_TIMEOUT);
            log.info("获取网关当前配置:\r\n{}",configInfo);
//            List<RouteDefinition> definitionList = JSON.parseArray(configInfo, RouteDefinition.class);
            //暂时只支持监听yml格式的配置变化
            Map<String, Object> data = nacosDataYamlParser.parseNacosData(configInfo, "yaml");
            log.info("获取到的配置MAP: {}", data.toString());
            List<RouteDefinition> definitionList = parse(data);
            for(RouteDefinition definition : definitionList){
                log.info("update route : {}",definition.toString());
                routeChangeEvent.add(definition);
            }
        } catch (Exception e) {
            log.error("初始化网关路由时发生错误",e);
        }
        dynamicRouteByNacosListener(nacosConfig.NACOS_ROUTE_DATA_ID,nacosConfig.NACOS_ROUTE_GROUP);
    }

    /**
     * 监听Nacos下发的动态路由配置
     * @param dataId
     * @param group
     */
    public void dynamicRouteByNacosListener (String dataId, String group){
        try {
            configService.addListener(dataId, group, new Listener()  {
                @Override
                public void receiveConfigInfo(String configInfo) {
                    try {
                        log.info("进行网关更新:\n\r{}", configInfo);
//                    List<RouteDefinition> definitionList = JSON.parseArray(configInfo, RouteDefinition.class);
                        //暂时只支持监听yml格式的配置变化
                        if (!dataId.endsWith("yml") && !dataId.endsWith("yaml")) {
                            log.error("dataId must be yml format!");
                            return;
                        }
                        Map<String, Object> data = nacosDataYamlParser.parseNacosData(configInfo, "yaml");
                        List<RouteDefinition> definitionList = parse(data);
                        for (RouteDefinition definition : definitionList) {
                            log.info("update route : {}", definition.toString());
                            routeChangeEvent.update(definition);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                @Override
                public Executor getExecutor() {
                    log.info("getExecutor\n\r");
                    return null;
                }
            });
        } catch (NacosException e) {
            log.error("从nacos接收动态路由配置出错!!!",e);
        }
    }

    /**
     * 将Map格式的配置信息装配成RouteDefinition队列对象
     */
    private List<RouteDefinition> parse(Map<String, Object> data){

        List<String> routes = data.keySet().stream().map(k -> k.split("\\.")[2]).distinct().collect(Collectors.toList());

        List<RouteDefinition> routeDefinitions = new ArrayList<>(routes.size());

        routes.forEach(r -> {
            List<String> filters = data.keySet().stream().filter(k -> k.startsWith("spring.gateway."+r+".filters"))
                    .map(k -> (String)data.get(k)).collect(Collectors.toList());
            List<String> paraWithPredicates = Lists.asList(data.get("spring.gateway."+r+".id")+"="+data.get("spring.gateway."+r+".uri"),
                    data.keySet().stream().filter(k -> k.startsWith("spring.gateway." + r + ".predicates"))
                            .map(k -> (String) data.get(k)).toArray(String[]::new));
            RouteDefinition rd = new RouteDefinition(Joiner.on(",").join(paraWithPredicates));

            List<FilterDefinition> filterDefinitions = new ArrayList<>(filters.size());
            filters.forEach(f -> filterDefinitions.add(new FilterDefinition(f)));

            rd.setFilters(filterDefinitions);

            routeDefinitions.add(rd);
        });

        return routeDefinitions;
    }
}
